package server

import (
	"github.com/devfeel/dotweb"
	"net/url"
	"git.oschina.net/YPTArch/usercenter/oauth2/models"
	server "git.oschina.net/YPTArch/usercenter/models"
	"git.oschina.net/YPTArch/usercenter/oauth2/errors"
	"git.oschina.net/YPTArch/usercenter/oauth2/consts"
	"net/http"
	"time"
	"git.oschina.net/YPTArch/usercenter/oauth2/generates"
	"fmt"
	"git.oschina.net/YPTArch/usercenter/oauth2"
	"git.oschina.net/YPTArch/usercenter/oauth2/store"
	"git.oschina.net/YPTArch/usercenter/libs/data"
	"git.oschina.net/YPTArch/usercenter/remoteserver/rpcremote/appcenter"
	"git.oschina.net/YPTArch/usercenter/remoteserver/httpremote/tokenserver"
	"github.com/devfeel/dotweb/framework/crypto"
	"strings"
	"git.oschina.net/YPTArch/basic/sys/logs"
)

func UseOAuth2(dotWeb *dotweb.DotWeb) {
	InitRoute(dotWeb.HttpServer)
}
func InitRoute(httpServer *dotweb.HttpServer) {

	//oauth2.0
	/*httpServer.Router().GET("/usercenter/login", loginHandler)
	httpServer.Router().POST("/usercenter/login", loginSubmitHandler)*/

	httpServer.Router().Any("/usercenter/authorize", authorizeHandler)

	httpServer.Router().Any("/usercenter/token", tokenHandler)
	//httpServer.Router().POST("/usercenter/token", tokenHandler)
	/*
			//注册
			httpServer.Router().GET("/usercenter/register", registerHandler)
			httpServer.Router().POST("/usercenter/register", registerSubmitAndLoginHandler)*/
	
	

}
func ValidationAuthorizeRequest(ctx dotweb.Context) (req *models.AuthorizeRequest, err error) {
	redirectURI, err := url.QueryUnescape(ctx.FormValue("redirect_uri"))
	if err != nil {
		return nil, err
	}
	clientID, clientSecret := ctx.FormValue("client_id"), ctx.FormValue("client_secret")
	if clientID == "" || redirectURI == "" {
		err = errors.ErrInvalidRequest
		return nil, err
	}
	clientID,_clientSecret,err:= ClientInfoHandler(clientID)
	if err != nil {
		return nil,err
	}
	if strings.TrimSpace(clientSecret)!=_clientSecret{
		err=errors.ErrInvalidRequest
		return nil,err
	}


	resType := consts.ResponseType(ctx.FormValue("response_type"))
	if resType.String() == "" {
		err = errors.ErrUnsupportedResponseType
		return
	} else if allowed := consts.CheckResponseType(resType); !allowed {
		err = errors.ErrUnsupportedResponseType
		return
	}
	req = &models.AuthorizeRequest{
		RedirectURI:  redirectURI,
		ResponseType: resType,
		ClientID:     clientID,
		State:        ctx.FormValue("state"),
		Scope:        ctx.FormValue("scope"),
		UserID:       ctx.FormValue("user_id"),
	}

	return
}

// 得到认证授权code
func GetAuthorizeToken(req *models.AuthorizeRequest) (ti models.TokenInfo, err error) {
	tgr := &models.TokenGenerateRequest{
		ClientID:    req.ClientID,
		UserID:      req.UserID,
		RedirectURI: req.RedirectURI,
		Scope:       req.Scope,
	}
	//保存
	ti, err = GenerateAuthToken(req.ResponseType, tgr)
	if err != nil {
		return nil, err
	}
	if ti == nil {
		err = errors.ErrInvalidRequest
		return nil, err
	}
	return
}

//生成授权token(code)
func GenerateAuthToken(rt consts.ResponseType, tgr *models.TokenGenerateRequest) (ti models.TokenInfo, err error) {
	gb := &generates.GenerateBasic{
		ClientID:     tgr.ClientID,
		ClientSecret: tgr.ClientSecret,
		CreateAt:     time.Now(),
	}
	ti = models.NewToken()
	ti.SetClientID(tgr.ClientID)
	ti.SetUserID(tgr.UserID)
	ti.SetRedirectURI(tgr.RedirectURI)
	ti.SetScope(tgr.Scope)
	switch rt {
	case consts.Code: //生成code
		tv, err := generates.NewAuthorizeGenerate().Token(gb)
		if err != nil {
			return nil, err
		}
		ti.SetCode(tv)
		ti.SetCodeExpiresIn(oauth2.DefaultCodeExp)
		ti.SetCodeCreateAt(gb.CreateAt)
	case consts.Token: //生成token
		cfg := oauth2.DefaultImplicitTokenCfg
		tv, _, err := generates.NewAccessGenerate().Token(gb, cfg.IsGenerateRefresh)
		if err != nil {
			return nil, err
		}
		ti.SetAccess(tv)
		ti.SetAccessCreateAt(gb.CreateAt)
		ti.SetAccessExpiresIn(cfg.AccessTokenExp)
	}
	// TODO 保存 tokenInfo
	store.GetStoreServer().Create(ti)

	return
}
func saveToken(ctx dotweb.Context, ti models.TokenInfo) {
	if ti.GetCode() != "" {
		ctx.AppContext().Set(ti.GetCode(), ti)
	}
	if ti.GetAccess() != "" {
		ctx.AppContext().Set(ti.GetAccess(), ti)
	}
	if ti.GetRefresh() != "" {
		ctx.AppContext().Set(ti.GetRefresh(), ti)
	}
}
func getToken(ctx dotweb.Context, key string) models.TokenInfo {
	res, isExists := ctx.AppContext().Get(key)
	if isExists {
		return nil
	}
	ti := res.(models.TokenInfo)
	return ti
}

//授权服务器
//1.当前client的secret是否为空
//2.检查传入的callback与该client的callback是否一致 (如果client有默认回调地址 1.检查该次回调地址和默认地址是否属于同一个域,过滤其它应用的恶意请求)
//3.检查传入的secret与该client的secret是否一致 (如果client带有secret 表明自己身份, 根据身份进行相应验证)
func authorizeHandler(ctx dotweb.Context) error {
	req, err := ValidationAuthorizeRequest(ctx)
	if err != nil {
		ctx.WriteJson(CustomErrorResultHandler(err.Error()))
		return nil
	}
	
	if ctx.Request().Method == "POST" {
		userId, err := PasswordAuthorizationHandler(ctx.FormValue("LoginName"), ctx.FormValue("LoginPwd"))
		if err !=nil{
			ctx.WriteJson(CustomErrorResultHandler(err.Error()))
			return nil
		}
		if userId != "" {
			req.UserID = userId
			ti, err := GetAuthorizeToken(req)
			if err != nil {
				ctx.WriteJson(CustomErrorResultHandler(err.Error()))
				return nil
			}
			// TODO 保存ti
			//saveToken(ctx, ti)
			err= CustomTokenSave(ti)
			if err != nil {
				ctx.WriteJson(CustomErrorResultHandler(err.Error()))
				return nil
			}
			data := GetAuthorizeData(req.ResponseType, ti)
			url, _ := GetRedirectURI(req, data)
			ctx.Redirect(http.StatusFound, url)
			return nil
		}
	} else {
		//TODO 判断请求 为 code\token
		// 1.code:  跳转到认证服务器login->登录授权->回调返回code->客户端带上code请求Token服务器->Token服务器返回Token
		// 2.token: 跳转到认证服务器login->登录授权->认证服务器跳转到Token服务器->Token服务器返回Token
		//跳转到认证服务器login
		ctx.ViewData().Set("appName", "ERP")
		ctx.ViewData().Set("urlPrams", "/usercenter/register?"+ctx.Request().URL.RawQuery)
		ctx.View("oauth2/static/login.html")
	}
	return nil

}

func PasswordAuthorizationHandler(name, pwd string) (string, error) {
	if len(strings.TrimSpace(name))==0{
		logs.Logger.Error("用户名不存在")
		return "",errors.ErrUserNameNotNil
	}
	if len(strings.TrimSpace(pwd))==0{
		logs.Logger.Error("用户名不存在")
		return "",errors.ErrUserPwdNotNil
	}
	user,err:= server.UserService.GetUserByLogin(name)
	if err!=nil{
		return "",err
	}
	if user==nil{
		logs.Logger.Error("用户名不存在")
		return "",errors.ErrUserNameNotExist
	}
	if user.UserPwd!=cryptos.GetMd5String(pwd){
		logs.Logger.Error("用户名密码错误")
		return "",errors.ErrUserPwd
	}
	//验证权限
	return user.UserId,nil
}

func ClientInfoHandler(clientID string)(string, clientSecret string, err error)  {
	app:= appcenter.RemoteGetApp(clientID)
	if app==nil{
		return "","",errors.ErrInvalidClient
	}
	return app.AppId,app.AppSecret,nil
}

// 验证token请求是否合法,并返回token请求参数
func ValidationTokenRequest(ctx dotweb.Context) (gt consts.GrantType, tgr *models.TokenGenerateRequest, err error) {

	if v := ctx.Request().Method; !(v == "POST" || v == "GET") {
		err = errors.ErrInvalidRequest
		return
	}
	// 验证grant type
	gt = consts.GrantType(ctx.FormValue("grant_type"))
	if gt.String() == "" {
		err = errors.ErrUnsupportedGrantType
		return
	} else if allowed := consts.CheckGrantType(gt); !allowed { //验证token type
		err = errors.ErrUnsupportedGrantType
		return
	}
	// TODO 验证client 相关信息

	clientID, clientSecret := ctx.FormValue("client_id"), ctx.FormValue("client_secret")
	if clientID == "" || clientSecret == "" {
		err = errors.ErrInvalidRequest
		return gt,nil, err
	}
	clientID,_clientSecret,err:= ClientInfoHandler(clientID)
	if err != nil {
		return gt,nil, err
	}
	if strings.TrimSpace(clientSecret)!=_clientSecret{
		err=errors.ErrInvalidRequest
		return gt,nil, err
	}
	tgr = &models.TokenGenerateRequest{
		ClientID:     clientID,
		ClientSecret: clientSecret,
	}

	switch gt {
	case consts.AuthorizationCode:
		tgr.RedirectURI = ctx.FormValue("redirect_uri")
		tgr.Code = ctx.FormValue("code")

		if tgr.RedirectURI == "" ||
			tgr.Code == "" {
			err = errors.ErrInvalidRequest
			return gt, nil, err
		}
	case consts.PasswordCredentials:
		tgr.Scope = ctx.FormValue("scope")
		username, password := ctx.FormValue("username"), ctx.FormValue("password")

		if username == "" || password == "" {
			err = errors.ErrInvalidRequest
			return gt, nil, err
		}
		userID, err := PasswordAuthorizationHandler(username, password)
		if err !=nil{
			return gt, nil, err
		} else if userID == "" {
			err = errors.ErrInvalidGrant
			return gt, nil, err
		}

		tgr.UserID = userID
	case consts.ClientCredentials:
		tgr.Scope = ctx.FormValue("scope")
	case consts.Refreshing:
		tgr.Refresh = ctx.FormValue("refresh_token")
		tgr.Scope = ctx.FormValue("scope")

		if tgr.Refresh == "" {
			err = errors.ErrInvalidRequest
		}
	}
	return
}

// 根据刷新Token生成 access token
func RefreshGenerateAccessToken(gt consts.GrantType, tgr *models.TokenGenerateRequest) (ti models.TokenInfo, err error) {
	// TODO 根据refreshToken 生成accessToken
	gb := &generates.GenerateBasic{
		ClientID:     tgr.ClientID,
		ClientSecret: tgr.ClientSecret,
		CreateAt:     time.Now(),
	}
	//得到配置文件
	var cfg = oauth2.DefaultAuthorizeCodeTokenCfg
	tv, rv, err := generates.NewAccessGenerate().Token(gb, cfg.IsGenerateRefresh)
	if err != nil {
		return nil, err
	}
	ti=models.NewToken()
	ti.SetAccess(tv)
	ti.SetAccessCreateAt(gb.CreateAt)
	ti.SetAccessExpiresIn(cfg.AccessTokenExp)
	if rv != "" {
		ti.SetRefresh(rv)
		ti.SetRefreshCreateAt(gb.CreateAt)
		ti.SetRefreshExpiresIn(cfg.RefreshTokenExp)
	}
	// TODO 保存生成access token
	store.GetStoreServer().Create(ti)
	return
}

func getConfig(gt consts.GrantType) (cfg *oauth2.Config) {
	switch gt {
	case consts.AuthorizationCode:
		cfg = oauth2.DefaultAuthorizeCodeTokenCfg
	case consts.PasswordCredentials:
		cfg = oauth2.DefaultPasswordTokenCfg
	case consts.ClientCredentials:
		cfg = oauth2.DefaultClientTokenCfg
	}
	return
}

// 生成 access token
func GenerateAccessToken(gt consts.GrantType, tgr *models.TokenGenerateRequest) (ti models.TokenInfo, err error) {
	ti = models.NewToken()
	gb := &generates.GenerateBasic{
		ClientID:     tgr.ClientID,
		ClientSecret: tgr.ClientSecret,
		UserID:       tgr.UserID,
		CreateAt:     time.Now(),
	}
	//得到配置文件
	var cfg = getConfig(gt)
	if cfg ==nil{
		logs.Logger.Error("未得到配置文件信息")
		return nil,errors.ErrInvalidRequest
	}

	ti.SetClientID(tgr.ClientID)
	ti.SetUserID(tgr.UserID)
	ti.SetRedirectURI(tgr.RedirectURI)
	ti.SetScope(tgr.Scope)
	ti.SetAccessCreateAt(gb.CreateAt)

	av, rv, err := generates.NewAccessGenerate().Token(gb, cfg.IsGenerateRefresh)
	if err != nil {
		return nil, err
	}
	ti.SetAccess(av)
	// set access token expires
	ti.SetAccessExpiresIn(cfg.AccessTokenExp)
	if rv != "" {
		ti.SetRefresh(rv)
		ti.SetRefreshCreateAt(gb.CreateAt)
		ti.SetRefreshExpiresIn(cfg.RefreshTokenExp)
	}
	// TODO 保存生成access token
	store.GetStoreServer().Create(ti)
	return ti, nil
}

// GetAccessToken access token
func GetAccessToken(gt consts.GrantType, tgr *models.TokenGenerateRequest) (ti models.TokenInfo, err error) {
	switch gt {
	case consts.AuthorizationCode: //code 生成token
		ati, err := GenerateAccessToken(gt, tgr)
		if err != nil {
			//判断异常类型
			if err == errors.ErrInvalidAuthorizeCode {
				err = errors.ErrInvalidGrant
			} else if err == errors.ErrInvalidClient {
				err = errors.ErrInvalidClient
			}
			return nil, err
		}
		ti = ati
	case consts.PasswordCredentials, consts.ClientCredentials:
		ati, err := GenerateAccessToken(gt, tgr)
		if err != nil {
			err = errors.ErrInvalidClient
			return nil, err
		}
		ti = ati
	case consts.Refreshing:
		rti, err := RefreshGenerateAccessToken(gt, tgr)
		if err != nil {
			if err == errors.ErrInvalidRefreshToken || err == errors.ErrExpiredRefreshToken {
				err = errors.ErrInvalidGrant
			}
			return nil, err
		}
		ti = rti
	}
	return
}

//token生成服务器
//1.验证client信息与授权服务器验证一致
func tokenHandler(ctx dotweb.Context) error {
	//TODO token处理
	// 1.AuthorizationCode:  得到回调地址与客户端传入的code-> 通过code 得到code生成时保存的登录信息 注code只能用于一次，并有一定的时效性
	// 2.PasswordCredentials 在验证token请求时并验证用户名与密码是否正确并返回userId, 以做生成token使用
	gt, tgr, err := ValidationTokenRequest(ctx)
	if err != nil {
		ctx.WriteJson(CustomErrorResultHandler(err.Error()))
		return nil
	}
	switch gt {
	case consts.AuthorizationCode: //如果是AuthorizationCode 时需判断code
		ti,err:=store.GetStoreServer().GetByCode(tgr.Code)
		if err !=nil{
			ctx.WriteJson(CustomErrorResultHandler(errors.ErrInvalidAuthorizeCode.Error()))
			return nil
		}

		if ti.GetRedirectURI() != tgr.RedirectURI {
			ctx.WriteJson(CustomErrorResultHandler(errors.ErrInvalidRedirectURI.Error()))
			return nil
		}
		tgr.UserID = ti.GetUserID()
		tgr.Scope = ti.GetScope()
	case consts.Refreshing:
		res,isExists:=ctx.AppContext().Once(tgr.Refresh)
		if !isExists{
			ctx.WriteJson(CustomErrorResultHandler(errors.ErrExpiredRefreshToken.Error()))
			return nil
		}
		ti := res.(models.TokenInfo)
		tgr.UserID=ti.GetUserID()
		tgr.Refresh=ti.GetRefresh()
		tgr.ClientID=ti.GetClientID()

	}
	ti, err := GetAccessToken(gt, tgr)
	if err!=nil{
		ctx.WriteJson(CustomErrorResultHandler(err.Error()))
		return nil
	}
	err= CustomTokenSave(ti)
	if err != nil {
		ctx.WriteJson(CustomErrorResultHandler(err.Error()))
		return nil
	}
	ctx.WriteJson(CustomResultHandler(GetTokenData(ti)))
	return nil
}

//自定义远程存储
func CustomTokenSave(ti models.TokenInfo) error  {
	return nil
	user,err:=server.UserService.GetUserById(ti.GetUserID())
	if err!=nil{
		return err
	}
	if user==nil{
		return errors.ErrUserNotExist
	}
	rst:=tokenserver.RemoteSaveTokenRequest{
		Access:ti.GetAccess(),
		AccessCreateAt:ti.GetAccessCreateAt().Unix(),
		AccessExpiresIn:ti.GetAccessExpiresIn().Nanoseconds(),
		User:user,
	}
	err= tokenserver.RemoteSaveToken(&rst)
	if err != nil {
		return err
	}
	return nil
}

// 得到认证授权返回数据
func GetAuthorizeData(rt consts.ResponseType, ti models.TokenInfo) (data map[string]interface{}) {
	if rt == consts.Code {
		data = map[string]interface{}{
			"code": ti.GetCode(),
		}
	} else {
		data = GetTokenData(ti)
	}
	return
}

func ExtensionFieldsHandler(ti models.TokenInfo) map[string]interface{}  {
	user,_:=server.UserService.GetUserById(ti.GetUserID())
	return map[string]interface{}{
		"user_id": ti.GetUserID(),
		"fore_updatepwd":user.UserForeUpdatePwd,
	}
}

// GetTokenData token data
func GetTokenData(ti models.TokenInfo) (data map[string]interface{}) {
	data = map[string]interface{}{
		"access_token": ti.GetAccess(),
		//"token_type":   "", //s.Config.TokenType,
		"expires_in": ti.GetAccessExpiresIn(),
	}
	if scope := ti.GetScope(); scope != "" {
		data["scope"] = scope
	}

	if refresh := ti.GetRefresh(); refresh != "" {
		data["refresh_token"] = refresh
	}

	if fn := ExtensionFieldsHandler; fn != nil {
		ext := fn(ti)
		for k, v := range ext {
			if _, ok := data[k]; ok {
				continue
			}
			data[k] = v
		}
	}
	return
}

// 根据权限请求得到跳转url
func GetRedirectURI(req *models.AuthorizeRequest, data map[string]interface{}) (uri string, err error) {
	u, err := url.Parse(req.RedirectURI)
	if err != nil {
		return
	}
	q := u.Query()
	if req.State != "" {
		q.Set("state", req.State)
	}

	for k, v := range data {
		q.Set(k, fmt.Sprint(v))
	}
	switch req.ResponseType {
	case consts.Code:
		u.RawQuery = q.Encode()
	case consts.Token:
		u.RawQuery = ""
		u.Fragment, err = url.QueryUnescape(q.Encode())
		if err != nil {
			return
		}
	}
	uri = u.String()
	return
}

//自定义返回
func CustomResultHandler (d map[string]interface{})(result interface{}){
	r:=new(data.Result)
	r.SuccessResult(d)
	return  r
}
//自定义错误返回
func CustomErrorResultHandler(msg string) (result interface{}) {
	r:=new(data.Result)
	r.FailResult(msg)
	return  r
}